1 00:00:00,140 --> 00:00:00,770 Hey there. 2 00:00:00,770 --> 00:00:01,640 Welcome back. 3 00:00:01,640 --> 00:00:07,790 In this lecture we're going to be learning about Parallel Lewis, which is a feature in Roblox Studio 4 00:00:07,790 --> 00:00:11,720 that allows us to execute Lua code on multiple threads. 5 00:00:11,720 --> 00:00:17,960 This is not the same as a coroutine thread, because coroutine threads are executed in serial and not 6 00:00:17,960 --> 00:00:18,860 in parallel. 7 00:00:18,860 --> 00:00:22,670 Let's first get a definition of what parallel and serial execution is. 8 00:00:22,670 --> 00:00:26,600 Serial execution is what you are used to when programming in studio. 9 00:00:26,600 --> 00:00:32,330 Serial means that code is executed in a sequence line by line, one after the other. 10 00:00:32,330 --> 00:00:37,610 Serial means that the code has to wait for previous code to execute before moving on to the next set 11 00:00:37,610 --> 00:00:38,270 of code. 12 00:00:38,270 --> 00:00:44,330 Parallel execution, on the other hand, allows us to execute code simultaneously on multiple processor 13 00:00:44,330 --> 00:00:44,840 threads. 14 00:00:44,840 --> 00:00:50,180 This means we do not have to wait for a previous task to finish executing before starting the next task. 15 00:00:50,180 --> 00:00:55,550 Parallel execution is great for when we need to do a bunch of calculations, and we can spread the work 16 00:00:55,550 --> 00:01:00,980 of those calculations across multiple processor threads, which can help improve the frame time of our 17 00:01:00,980 --> 00:01:01,490 game. 18 00:01:01,490 --> 00:01:07,550 We can observe the frame time of our game by opening a tool called the Micro Profiler, which allows 19 00:01:07,550 --> 00:01:13,580 us to observe each frame that is rendered and what exactly is being rendered or executed in each frame. 20 00:01:13,580 --> 00:01:20,510 Now, this tells us we can open the Micro Profiler using Ctrl, alt F6 in studio or on the client. 21 00:01:20,510 --> 00:01:26,330 When we're playing in the Roblox player and when open, a menu bar is visible at the top of the game 22 00:01:26,330 --> 00:01:26,780 view. 23 00:01:26,780 --> 00:01:32,180 Under it, there is a moving bar graph which reflects the time used on each frame of the task scheduler. 24 00:01:32,180 --> 00:01:37,340 As they pass, the most recent frames appear on the right and flow to the left. 25 00:01:37,340 --> 00:01:42,500 So here inside of studio, we can go ahead and open up the Micro Profiler by pressing Ctrl, alt and 26 00:01:42,500 --> 00:01:43,310 F6. 27 00:01:43,310 --> 00:01:48,980 And there you can see our beautiful Micro Profiler as it is recording all of the frames that are currently 28 00:01:48,980 --> 00:01:49,760 being rendered. 29 00:01:49,760 --> 00:01:55,820 So each one of these individual orange bars represents a frame, and the height of the bar represents 30 00:01:55,820 --> 00:01:59,420 how long it took for that particular frame to completely execute. 31 00:01:59,420 --> 00:02:04,430 Now we are able to pause and observe these frames by pressing control and p. 32 00:02:04,580 --> 00:02:09,710 And now this screen should pop up and you will notice that we are no longer recording frames. 33 00:02:09,710 --> 00:02:14,240 Now we can move our mouse around and look at each one of these frames, and it tells us specifically 34 00:02:14,240 --> 00:02:15,230 what frame it was like. 35 00:02:15,230 --> 00:02:17,000 This is frame 439. 36 00:02:17,000 --> 00:02:18,320 This is frame 440. 37 00:02:18,320 --> 00:02:21,500 And it tells us the CPU time for each one of these frames. 38 00:02:21,500 --> 00:02:26,870 And this little green area tells us where we are currently observing in this panel right here. 39 00:02:27,110 --> 00:02:31,340 So this panel is going to show us what exactly was executed in a particular frame. 40 00:02:31,340 --> 00:02:34,130 And we can use our scroll wheel to zoom out. 41 00:02:34,130 --> 00:02:36,800 So let's go ahead and start zooming out. 42 00:02:36,800 --> 00:02:41,900 And you should see at the top as well that our little green view area has increased because now we're 43 00:02:41,900 --> 00:02:44,000 looking at multiple different frames. 44 00:02:44,000 --> 00:02:48,500 So each one of these little vertical bars represents the start and end of a frame. 45 00:02:48,500 --> 00:02:49,760 So this is a frame. 46 00:02:49,760 --> 00:02:50,480 This is a frame. 47 00:02:50,480 --> 00:02:51,530 This is a frame. 48 00:02:51,530 --> 00:02:56,870 And then all of this other stuff is tasks that are being executed in every single frame. 49 00:02:56,870 --> 00:03:01,790 You'll also notice on the left hand side each of these different labels like one's called GPU. 50 00:03:01,790 --> 00:03:04,790 One is called RGB worker, RGB worker. 51 00:03:04,790 --> 00:03:06,020 And then we keep going down. 52 00:03:06,020 --> 00:03:07,460 There's more RGB workers. 53 00:03:07,460 --> 00:03:13,070 And then there's another thread here called main slash render and then some other miscellaneous ones. 54 00:03:13,070 --> 00:03:18,320 So each one of these labels represents a thread, a thread on your processor. 55 00:03:18,320 --> 00:03:23,930 The number of threads being displayed here is going to depend on the processor inside of your computer. 56 00:03:23,930 --> 00:03:30,410 Most processors nowadays are quad cores or hexacore with multithreading, meaning every single core 57 00:03:30,410 --> 00:03:32,840 in the processor gets two threads. 58 00:03:32,840 --> 00:03:36,980 If you have a quad core with Hyperthreading, that means you're going to have a total of eight threads, 59 00:03:36,980 --> 00:03:42,380 while if you have a hexa core, you're going to have 12 threads inside of the Roblox documentation. 60 00:03:42,380 --> 00:03:45,800 It tells us what each of these labels for the threads mean. 61 00:03:45,800 --> 00:03:52,730 For example, the main process is input humanoids, animation, physics, sounds, update studio interfaces, 62 00:03:52,730 --> 00:03:53,240 blah blah blah. 63 00:03:53,240 --> 00:03:55,760 So this works on basically all the main stuff. 64 00:03:55,760 --> 00:04:01,490 And then these other ones labeled worker helps our main thread with other tasks that it needs to calculate. 65 00:04:01,490 --> 00:04:05,900 And then we also have a section for rendering such as our GPU for preparing. 66 00:04:05,900 --> 00:04:10,550 It says information from the main thread is used to update rendering models, perform issue rendering 67 00:04:10,550 --> 00:04:15,530 commands including 2D interfaces, and then present synchronizes with the graphics card. 68 00:04:15,530 --> 00:04:18,020 This is some internal stuff we don't need to worry about now. 69 00:04:18,020 --> 00:04:23,030 Any time we have a script executing in our game, such as a local script on the client. 70 00:04:23,030 --> 00:04:29,030 Depending on how long it took for a local script to execute, it will appear inside of our micro profiler 71 00:04:29,030 --> 00:04:30,710 depending on what it's doing. 72 00:04:30,710 --> 00:04:36,170 So for example, if you have any scripts that are executing or listening to events after, let's say, 73 00:04:36,170 --> 00:04:42,620 the heartbeat, then it's going to show up in a section called a deferred threads if you have deferred 74 00:04:42,620 --> 00:04:43,760 signaling enabled. 75 00:04:43,760 --> 00:04:47,780 So for example, if we take a look through here, let's go ahead and see if we can find the different 76 00:04:47,780 --> 00:04:49,070 run service events. 77 00:04:49,070 --> 00:04:55,130 So in the main slash render section there should be an area specifically for the run service render 78 00:04:55,130 --> 00:04:55,910 stepped event. 79 00:04:55,910 --> 00:04:59,810 So let's see if we can find it okay I see a render render step in. 80 00:04:59,950 --> 00:05:00,760 Colonel. 81 00:05:00,760 --> 00:05:01,450 Here we go. 82 00:05:01,450 --> 00:05:02,230 Run service! 83 00:05:02,230 --> 00:05:03,370 Render stepped. 84 00:05:03,370 --> 00:05:07,810 And this tells us all the bunch of other stuff that is executing underneath the render stepped. 85 00:05:07,810 --> 00:05:09,970 And then there's going to be other stuff executing. 86 00:05:09,970 --> 00:05:13,240 If we connect any functions to the render step event. 87 00:05:13,240 --> 00:05:16,120 And then there's also going to be the stepped event and the heartbeat. 88 00:05:16,120 --> 00:05:20,680 We can see the heartbeat up here being executed on RHB worker 13. 89 00:05:20,680 --> 00:05:26,530 But any time you connect a function to the render stepped event, it's going to appear under the main 90 00:05:26,530 --> 00:05:28,240 slash render thread. 91 00:05:28,240 --> 00:05:33,310 So as an example, I have a local script and replicated first that is called my local script one. 92 00:05:33,310 --> 00:05:37,270 There's nothing in either of these local scripts, but in my first one we can go ahead and do is grab 93 00:05:37,270 --> 00:05:38,440 the run service. 94 00:05:40,000 --> 00:05:43,420 And let's go ahead and connect to the render stepped event. 95 00:05:43,420 --> 00:05:45,730 So render stepped connect a function. 96 00:05:46,000 --> 00:05:52,510 And then to be able to see this inside of our micro profiler I want this function to do a bunch of garbage. 97 00:05:52,510 --> 00:05:56,620 That way it kind of extends the frame time or how long it takes to execute. 98 00:05:56,620 --> 00:06:02,590 So what we can go ahead and do is create a for loop and have it iterate, let's say 100,000 times. 99 00:06:02,590 --> 00:06:06,370 And we'll do something random in here, like let's do a math calculation. 100 00:06:06,580 --> 00:06:13,060 We'll create a variable called a and we'll just set it equal to let's say the square root of I times 101 00:06:13,060 --> 00:06:15,790 I just a simple basic stupid math calculation. 102 00:06:15,790 --> 00:06:22,150 That way we're wasting resources for the processor and hopefully it'll appear inside of the micro profiler. 103 00:06:22,630 --> 00:06:28,900 So if we play test our game and wait for our player to spawn in, and then let's go ahead and unpause 104 00:06:28,900 --> 00:06:34,660 the micro profiler by pressing control and P, let's record some frames and then let's pause it again. 105 00:06:34,750 --> 00:06:37,150 And then we can select one of these frames. 106 00:06:37,150 --> 00:06:40,540 And let me go ahead and zoom out because I think we're zoomed in way too much. 107 00:06:41,380 --> 00:06:46,000 And then let's go ahead and move down to the main slash render. 108 00:06:46,150 --> 00:06:48,850 And let's find render stepped in here. 109 00:06:49,270 --> 00:06:49,720 Okay. 110 00:06:49,720 --> 00:06:50,110 Perfect. 111 00:06:50,110 --> 00:06:52,420 Here is a run service render stepped. 112 00:06:52,420 --> 00:06:56,980 And here is our local script appearing under our render stepped. 113 00:06:56,980 --> 00:06:58,960 So it took our local script. 114 00:06:58,960 --> 00:07:06,010 That function connected to it about 0.6 milliseconds to execute 100,000 iterations through that loop. 115 00:07:06,010 --> 00:07:08,380 And it's doing that every single frame. 116 00:07:08,380 --> 00:07:11,920 So if we move on to the next frame, let me go ahead and zoom out. 117 00:07:12,370 --> 00:07:13,990 Let's go over to the next frame. 118 00:07:13,990 --> 00:07:16,990 Here we can also see our local script. 119 00:07:17,020 --> 00:07:21,820 Now currently in my workspace, if we go to the signal behavior it's set to immediate. 120 00:07:21,820 --> 00:07:27,850 If we set this to deferred mode instead of the local script appearing underneath the run service render 121 00:07:27,850 --> 00:07:33,580 step event, it's going to appear in an area after the event called Deferred Threads. 122 00:07:33,670 --> 00:07:39,550 So let me go ahead and stop playtesting and let's swap signal behavior back to deferred. 123 00:07:39,910 --> 00:07:44,890 Let's unpause the Micropro filer by pressing control and P record some frames, and then we'll pause 124 00:07:44,890 --> 00:07:45,610 it again. 125 00:07:46,090 --> 00:07:50,110 And let's go ahead and take a look at the main slash render thread. 126 00:07:50,350 --> 00:07:53,440 And now we have this section appearing called deferred threads. 127 00:07:53,440 --> 00:07:55,540 Let's see if we can find render stepped okay. 128 00:07:55,540 --> 00:07:56,110 Perfect. 129 00:07:56,110 --> 00:07:57,490 Render stepped is over here. 130 00:07:57,490 --> 00:08:02,080 And then afterwards any functions connected to the render stepped event get put in that queue. 131 00:08:02,080 --> 00:08:08,680 If you watched my video about deferred events so it's placed in this deferred threads queue. 132 00:08:08,680 --> 00:08:10,780 And sometime later there we go. 133 00:08:10,780 --> 00:08:15,490 Here is our local script number one taking 0.6 milliseconds to execute. 134 00:08:15,490 --> 00:08:17,710 And it's doing that every single frame. 135 00:08:17,710 --> 00:08:22,060 Now what if we want to be able to add a label to this local script, for example? 136 00:08:22,060 --> 00:08:27,130 All of these other stuff have cool labels like this one says Update Light Grid and this one says shadows, 137 00:08:27,130 --> 00:08:28,990 and this one says Update Perform. 138 00:08:28,990 --> 00:08:31,300 How do we add a label to our local script? 139 00:08:31,300 --> 00:08:35,680 Well, it's easy to do and we can do it using the debug library. 140 00:08:35,680 --> 00:08:39,100 So inside of our local script we can go ahead and type out debug. 141 00:08:39,100 --> 00:08:44,500 And it says this library provides functions useful for debugging and profiling code. 142 00:08:44,500 --> 00:08:47,590 And inside of the debug library there is a function called profile. 143 00:08:47,590 --> 00:08:51,550 Begin starts profiling for a micro profiler label. 144 00:08:51,550 --> 00:08:52,960 And we can pass a label here. 145 00:08:52,960 --> 00:08:58,240 So we'll give this one a name or label of render stepped work. 146 00:08:58,810 --> 00:09:04,660 And then to close out this profile we need to call another function called profile end. 147 00:09:04,990 --> 00:09:11,800 So any code that appears between these two function calls is going to appear inside of our micro profiler 148 00:09:11,800 --> 00:09:13,480 with this particular label. 149 00:09:14,020 --> 00:09:15,700 Let's go ahead and unpause. 150 00:09:17,280 --> 00:09:18,810 And then repose. 151 00:09:18,810 --> 00:09:21,810 And let's go down to the main thread. 152 00:09:22,140 --> 00:09:22,950 And there we go. 153 00:09:22,950 --> 00:09:23,820 Local script one. 154 00:09:23,820 --> 00:09:26,670 Here is our label of render stepped work. 155 00:09:26,670 --> 00:09:32,340 So this easily allows you to be able to track any kind of processes that you have going on in your local 156 00:09:32,340 --> 00:09:32,760 scripts. 157 00:09:32,760 --> 00:09:37,530 So for example, if you rely heavily on a bunch of visual effects on the client side that need to be 158 00:09:37,530 --> 00:09:43,260 updated every single frame, then it might be a good idea to place it between a profile. 159 00:09:43,260 --> 00:09:47,430 So that way down the line, if you're having any performance issues, you can go ahead and check to 160 00:09:47,430 --> 00:09:50,880 see if there is something taking a long time to execute. 161 00:09:50,880 --> 00:09:55,560 For example, this one's taking 0.8 milliseconds to execute every single frame. 162 00:09:55,830 --> 00:09:59,880 Let's go ahead and continue filling up our micro profiler with more labels. 163 00:09:59,880 --> 00:10:02,760 So in the run service let's connect to the stepped event. 164 00:10:03,270 --> 00:10:06,540 And let's do the exact same thing that we're doing in the render step event. 165 00:10:06,540 --> 00:10:08,880 So I'm just going to copy this and paste that there. 166 00:10:08,880 --> 00:10:11,670 But this time we're going to call this our stepped work. 167 00:10:12,120 --> 00:10:14,550 And then let's do the same thing for the heartbeat event. 168 00:10:14,550 --> 00:10:17,520 We'll connect a function and do the exact same thing. 169 00:10:17,520 --> 00:10:20,370 But this time we're going to call this our heartbeat work. 170 00:10:20,370 --> 00:10:25,440 So now we should have three different labels appearing in the Micro Profiler for render stepped work, 171 00:10:25,440 --> 00:10:27,180 stepped work, and heartbeat work. 172 00:10:27,820 --> 00:10:30,460 Let's go ahead and pause our micro profiler. 173 00:10:30,460 --> 00:10:32,950 And let's go ahead and see if we can find those different things. 174 00:10:32,950 --> 00:10:34,450 And I can already see one right here. 175 00:10:34,720 --> 00:10:40,690 Here we have our stepped work which is being handled by our worker nine and here's our local script 176 00:10:40,690 --> 00:10:44,500 one performing its stepped work underneath the deferred threads. 177 00:10:44,530 --> 00:10:46,120 Let's see if we can find heartbeat. 178 00:10:46,450 --> 00:10:52,480 Oh look, just a little bit later here is the heartbeat event which executes after the stepped event. 179 00:10:52,480 --> 00:10:55,630 And there's local script one performing heartbeat work. 180 00:10:55,630 --> 00:10:59,230 And each time these are taken about 0.6 milliseconds to execute. 181 00:10:59,230 --> 00:11:02,890 And then we can also go back down to the main render thread. 182 00:11:02,890 --> 00:11:03,850 Let's go ahead and find. 183 00:11:03,850 --> 00:11:06,880 There we go render stepped work for local script one. 184 00:11:07,120 --> 00:11:10,720 Now you should also be noticing how this executes one after the other. 185 00:11:10,720 --> 00:11:13,150 So first we execute our render step work. 186 00:11:13,150 --> 00:11:17,470 Then we execute our stepped work and then we execute our heartbeat work. 187 00:11:17,470 --> 00:11:24,040 Now to further demonstrate that Lua code executes sequentially or in serial, let's copy this and let's 188 00:11:24,040 --> 00:11:26,680 connect two more functions to our heartbeat event. 189 00:11:26,680 --> 00:11:30,100 But this time we're going to call this heartbeat work one. 190 00:11:30,100 --> 00:11:31,900 This one is going to be heartbeat work two. 191 00:11:31,900 --> 00:11:34,150 And this one's going to be heartbeat work three. 192 00:11:34,630 --> 00:11:40,480 Let's go ahead and pause the micro profiler and take a look inside of the deferred thread section. 193 00:11:40,480 --> 00:11:44,500 Here we have our local script executing each one of those heartbeat works. 194 00:11:44,500 --> 00:11:47,620 So heartbeat work three heartbeat work two, heartbeat work one. 195 00:11:47,620 --> 00:11:51,760 And it was handling it on our worker thread number zero. 196 00:11:51,880 --> 00:11:56,770 And then if we go ahead and zoom out and let's move over to the next frame, let's see if we can find 197 00:11:56,770 --> 00:11:57,730 where it's being executed. 198 00:11:57,730 --> 00:12:04,990 So this time it's being handled by our worker six here is heartbeat work 3 to 1 as well as our render 199 00:12:04,990 --> 00:12:06,220 step work happening down there. 200 00:12:06,220 --> 00:12:07,420 And our step work. 201 00:12:07,420 --> 00:12:09,430 Let's move to the next frame. 202 00:12:09,970 --> 00:12:14,440 It's also again being handled by our worker six there's our heartbeat work. 203 00:12:14,440 --> 00:12:17,050 Here's our step work and here's our render step work. 204 00:12:17,050 --> 00:12:22,420 Again, if you notice each one of these functions is being executed one after the other. 205 00:12:22,420 --> 00:12:26,320 So the first one executes, then the next one, then the next one. 206 00:12:26,800 --> 00:12:31,960 To make this even more prominent, let's go ahead and copy all of the code we have in our first local 207 00:12:31,960 --> 00:12:32,230 script. 208 00:12:32,230 --> 00:12:36,610 So let's just copy this and let's paste it in our second local script as well. 209 00:12:36,610 --> 00:12:42,040 So now we have two scripts doing the exact same thing connecting to the run service. 210 00:12:42,730 --> 00:12:47,080 If we go ahead and pause our micro profiler and let's go ahead and take a look. 211 00:12:47,080 --> 00:12:48,430 What do you notice? 212 00:12:48,430 --> 00:12:52,270 We have Local Script two executing each one of its heartbeat work. 213 00:12:52,270 --> 00:12:55,570 Then local script one also executing each one of its heartbeat work. 214 00:12:55,570 --> 00:12:58,990 And as you can see, it's one after the other, one after the other. 215 00:12:58,990 --> 00:13:00,850 Same thing with the step work. 216 00:13:00,850 --> 00:13:04,270 We have local script two and local Script one executing their step work. 217 00:13:04,270 --> 00:13:08,320 And same goes for render step work one after the other. 218 00:13:08,350 --> 00:13:12,370 Now you might start to see the problem we're going to encounter with this. 219 00:13:12,370 --> 00:13:17,920 And that is what if we have multiple of these local scripts executing at the same time, connected to 220 00:13:17,920 --> 00:13:20,350 all these heartbeat events and render step events? 221 00:13:20,350 --> 00:13:25,600 Well, eventually the size of this is going to be pushed out so far that it's going to start extending 222 00:13:25,600 --> 00:13:26,830 the frame times. 223 00:13:26,830 --> 00:13:30,640 So we're averaging currently 16 milliseconds of frame time. 224 00:13:30,640 --> 00:13:36,580 Well, if we have more and more and more stuff executing sequentially, the frame time is going to increase 225 00:13:36,580 --> 00:13:39,460 and it's going to cause a worse performance for our game. 226 00:13:39,460 --> 00:13:44,560 To demonstrate this, let's go ahead and make these four loops even more expensive to execute. 227 00:13:44,560 --> 00:13:49,930 Let's just add an extra zero and make this loop 1 million times for every single one. 228 00:13:51,230 --> 00:13:54,800 And then let's copy that and do the exact same thing for Local Script two as well. 229 00:13:54,800 --> 00:13:57,320 We'll just delete that and then paste the new code in there. 230 00:13:57,320 --> 00:14:00,830 So now we have these four loops executing a million times. 231 00:14:00,830 --> 00:14:03,800 Let's go ahead and see what happens to our performance. 232 00:14:03,890 --> 00:14:07,940 As you can see our frame time is now horrible. 233 00:14:07,940 --> 00:14:09,230 Look at our frame time average. 234 00:14:09,230 --> 00:14:15,170 We're averaging 67 milliseconds of frame time and the game is lagging like crazy. 235 00:14:15,200 --> 00:14:17,960 Let's go ahead and pause and then let's stop testing. 236 00:14:17,960 --> 00:14:19,550 So we stop lagging. 237 00:14:19,550 --> 00:14:22,130 And let's go ahead and take a look at these frames. 238 00:14:22,340 --> 00:14:28,370 And if you notice, look at how long it is taking to execute each one of these works sequentially, 239 00:14:28,370 --> 00:14:29,210 one after the other. 240 00:14:29,210 --> 00:14:32,660 It's completely pushing the frame time of our frames. 241 00:14:32,660 --> 00:14:37,190 This is 60 milliseconds from here to here and it's just awful performance. 242 00:14:37,190 --> 00:14:41,540 So you may be thinking, good heavens, how do we solve this performance conundrum? 243 00:14:41,540 --> 00:14:45,260 How can we spread out the work of each one of these heartbeat works? 244 00:14:45,260 --> 00:14:48,350 And these render stepped works into multiple different threads? 245 00:14:48,350 --> 00:14:50,120 Because look at RRB worker six. 246 00:14:50,120 --> 00:14:55,580 Here he is struggling to execute all of this stuff by himself, while all of these other worker threads 247 00:14:55,580 --> 00:14:57,200 are doing nothing at all. 248 00:14:57,200 --> 00:15:00,830 How can we spread the work across all of these different threads? 249 00:15:00,830 --> 00:15:02,750 Well, drum roll please. 250 00:15:02,750 --> 00:15:05,930 This is where parallel luoyue can come into play. 251 00:15:05,930 --> 00:15:13,250 So on the parallel Luoyue documentation page, it tells us with parallel luoyue you can run code on 252 00:15:13,250 --> 00:15:17,900 multiple threads simultaneously, which can improve the performance of your experience. 253 00:15:17,900 --> 00:15:23,120 As you expand your experience with more content, you can adopt this model to help maintain the performance 254 00:15:23,120 --> 00:15:25,370 and safety of your Luoyue scripts. 255 00:15:25,370 --> 00:15:29,270 And they give us an example down here of a terrain generation system. 256 00:15:29,270 --> 00:15:34,760 While executing serially, you can see that it's having 60 millisecond frame times in its lagging the 257 00:15:34,760 --> 00:15:40,190 game, but if you script it in a way where it is executing on multiple different threads, now the game 258 00:15:40,190 --> 00:15:43,370 is performing well with a 17 millisecond frame time. 259 00:15:43,370 --> 00:15:48,080 So they tell us by default scripts execute sequentially, which is what we just saw. 260 00:15:48,080 --> 00:15:50,420 Code executes one after the other. 261 00:15:50,420 --> 00:15:56,000 If your experience has complex logic or content such as non-player characters, raycasting validation, 262 00:15:56,000 --> 00:16:02,210 and procedural generation, then sequential execution might cause lag for your users, which we just 263 00:16:02,210 --> 00:16:02,750 observed. 264 00:16:02,750 --> 00:16:08,240 With the parallel programming model, you can split tasks into multiple scripts and run them in parallel. 265 00:16:08,240 --> 00:16:13,700 This makes your experience code run faster, which improves the user experience, and it tells us to 266 00:16:13,700 --> 00:16:15,380 split code into multiple threads. 267 00:16:15,380 --> 00:16:20,690 We have to put our scripts in actors called an actor instance. 268 00:16:20,720 --> 00:16:25,790 If we look at the documentation page for what an actor is, it tells us that an actor is a container 269 00:16:25,790 --> 00:16:29,240 for code that can be safely split into its own thread. 270 00:16:29,240 --> 00:16:34,010 So those different worker threads using the task dot d synchronize function. 271 00:16:34,010 --> 00:16:37,190 It should also contain the instances used by its scripts. 272 00:16:37,370 --> 00:16:42,980 So back in studio, let's go ahead and create a new actor instance inside of replicated first so we 273 00:16:42,980 --> 00:16:44,420 can just search for actor. 274 00:16:44,420 --> 00:16:49,070 Here is our actor instance and we need to make our scripts a child of this actor. 275 00:16:49,070 --> 00:16:52,850 So let's go ahead and put our scripts as a child of this actor. 276 00:16:52,850 --> 00:16:55,580 And you might be thinking, great, we're done. 277 00:16:55,580 --> 00:16:57,320 No more performance problems, right? 278 00:16:57,320 --> 00:17:02,990 Well, you wish it was that easy because if we go and playtest our game, as you can see, we're still 279 00:17:02,990 --> 00:17:05,330 running into performance problems. 280 00:17:05,330 --> 00:17:12,470 And if we take a look at the micro profiler and we pause it, our code is still being executed sequentially 281 00:17:12,470 --> 00:17:15,980 one after the other, causing super high frame times. 282 00:17:16,220 --> 00:17:21,740 The reason for this is because we are not executing this code in parallel. 283 00:17:21,740 --> 00:17:27,320 In order to execute this code in parallel, we should replace this connect function here with the connect 284 00:17:27,320 --> 00:17:32,810 parallel function, which allows us to immediately run the code inside of here in parallel. 285 00:17:32,810 --> 00:17:39,230 So let me go ahead and hold down alt and then click to add multiple of these cursors. 286 00:17:39,230 --> 00:17:42,110 And we're going to change all of these to connect parallel. 287 00:17:42,200 --> 00:17:46,370 And then let's go ahead and copy that and do that for Local Script two as well. 288 00:17:46,430 --> 00:17:51,980 And the Roblox documentation tells us that instead of having to use the task dot d synchronize function, 289 00:17:51,980 --> 00:17:55,520 we can use the script signal connect parallel method. 290 00:17:55,520 --> 00:18:00,680 When you want to schedule a signal callback to immediately run your code in parallel upon triggering. 291 00:18:00,680 --> 00:18:01,820 So that's what we just did. 292 00:18:01,820 --> 00:18:07,670 We swapped all of the connect functions for connect parallel to start executing the code in parallel. 293 00:18:07,670 --> 00:18:10,100 So now you may be thinking great, we're done. 294 00:18:10,100 --> 00:18:10,550 Right? 295 00:18:10,550 --> 00:18:13,820 Well let's go play test our game and see what happens. 296 00:18:14,000 --> 00:18:16,910 And uh oh our game is still lagging. 297 00:18:16,910 --> 00:18:17,750 What's going on? 298 00:18:17,750 --> 00:18:19,400 If we check the micro profiler. 299 00:18:19,400 --> 00:18:20,030 Whoa. 300 00:18:20,060 --> 00:18:22,520 The frame times, they're still out of control. 301 00:18:22,520 --> 00:18:23,210 Hold on. 302 00:18:23,210 --> 00:18:26,060 Let's pause this and let's stop the test. 303 00:18:26,300 --> 00:18:27,590 What is this? 304 00:18:27,590 --> 00:18:30,440 Our code is still executing one after the other. 305 00:18:30,440 --> 00:18:34,670 There is no code being split up across multiple different threads. 306 00:18:34,670 --> 00:18:35,510 What is happening? 307 00:18:35,510 --> 00:18:38,270 Well, we actually need to do one more thing. 308 00:18:38,270 --> 00:18:44,990 In fact, what we need to do is we need to split our local scripts or separate them into multiple different 309 00:18:44,990 --> 00:18:47,450 actors instead of having both scripts. 310 00:18:47,450 --> 00:18:50,750 As a child of one actor, let's split them up into. 311 00:18:50,970 --> 00:18:56,730 Two actors to execute on separate threads, because right now what's happening is the code in both of 312 00:18:56,730 --> 00:19:02,070 these local scripts are going to still be executing on the same thread because they're in the same actor. 313 00:19:02,070 --> 00:19:08,730 So let's add another actor into our game and let's move local script two into that second actor. 314 00:19:08,730 --> 00:19:14,130 Now we should have the code and local script one execute in a different thread compared to the code 315 00:19:14,130 --> 00:19:15,750 in our local script. 316 00:19:15,750 --> 00:19:21,900 Number two now we are still lagging a bit, but if we go ahead and open up the micro profiler, what 317 00:19:21,900 --> 00:19:25,560 you will notice is that our frame times have been cut in half. 318 00:19:25,560 --> 00:19:32,020 So before we were averaging 60 to 70 milliseconds of frame time, and now we are averaging around 30 319 00:19:32,020 --> 00:19:34,020 to 40 milliseconds of frame time. 320 00:19:34,020 --> 00:19:36,180 So it was completely cut in half. 321 00:19:36,180 --> 00:19:41,670 Now the reason why we're still lagging is because the code executing in these local scripts are very 322 00:19:41,670 --> 00:19:44,820 stupid, and it's just wasting a whole bunch of resources. 323 00:19:44,820 --> 00:19:49,710 But if we take a look here, what is happening, our code is being split up into multiple different 324 00:19:49,710 --> 00:19:50,220 threads. 325 00:19:50,220 --> 00:19:57,090 So here you can see that local script one is executing an RX worker 13, while our other local script 326 00:19:57,090 --> 00:20:04,200 is executing an RX worker 14 and as we move along different frames, you can see how each of our tasks 327 00:20:04,200 --> 00:20:06,930 are being split up into different threads. 328 00:20:06,930 --> 00:20:08,670 And that's because we have two actors. 329 00:20:08,670 --> 00:20:12,870 And since we have two actors, we can split the code into two different threads. 330 00:20:13,080 --> 00:20:17,940 Now, let's be honest, you're probably never going to have this garbage work going on here where we're 331 00:20:17,940 --> 00:20:21,570 iterating through a loop 1 million times, doing who knows what. 332 00:20:21,570 --> 00:20:27,660 But let's go ahead and take a look at another example of how parallel Lua can improve the performance 333 00:20:27,660 --> 00:20:28,440 of our game. 334 00:20:28,440 --> 00:20:33,210 So let me go ahead and disable both of these local scripts so they're not impacting our performance 335 00:20:33,210 --> 00:20:33,840 anymore. 336 00:20:33,840 --> 00:20:36,630 And let's move on to the next example I would like to show you. 337 00:20:36,630 --> 00:20:39,840 So in this example here I have two deer. 338 00:20:40,110 --> 00:20:42,570 One of my deers is called serial deer. 339 00:20:42,660 --> 00:20:45,120 And my other deer is called parallel deer. 340 00:20:45,150 --> 00:20:51,540 The only difference between these two deer is that one of them has the script inside of an actor, while 341 00:20:51,540 --> 00:20:54,540 our serial deer just has the regular script. 342 00:20:54,540 --> 00:20:59,430 And the purpose of this script is just to move our deer to follow the closest player. 343 00:20:59,430 --> 00:21:02,940 So we're going to wait for a player to join the game and get their character. 344 00:21:02,940 --> 00:21:09,030 And then every single heartbeat, we're just moving, um, the deer towards the player's character. 345 00:21:09,030 --> 00:21:13,680 And inside of here, we're also calculating some random garbage right here, just to make sure that 346 00:21:13,680 --> 00:21:16,560 the frame time gets expanded for this particular heartbeat event. 347 00:21:16,560 --> 00:21:18,360 So we're doing some random garbage here. 348 00:21:18,480 --> 00:21:21,540 Let's say this is random calculations for an AI. 349 00:21:21,540 --> 00:21:25,740 And then we're calculating to see if we're too far away from the player. 350 00:21:25,740 --> 00:21:27,600 If we are, we'll just teleport to the player. 351 00:21:27,600 --> 00:21:31,140 Otherwise we'll have our humanoid move towards that player. 352 00:21:31,140 --> 00:21:36,960 And then we have a profile beginning and ending for our serial deer, and then for the script inside 353 00:21:36,960 --> 00:21:41,400 of our parallel deer, we're doing the exact same thing, but this time we're doing it in parallel. 354 00:21:41,400 --> 00:21:43,950 So we're still doing that random garbage. 355 00:21:43,950 --> 00:21:49,770 But this time, before we set the keyframe property for our deer, or before we have the humanoid move 356 00:21:49,770 --> 00:21:54,480 towards the player's character, we are calling the task Dot synchronize function, and the reason that 357 00:21:54,480 --> 00:22:00,000 we need to call the task dot synchronize function is to execute the code afterwards in serial. 358 00:22:00,000 --> 00:22:06,450 And that's because most functions and property inside of the Roblox API are not thread safe. 359 00:22:06,450 --> 00:22:08,280 What do I mean by thread safe? 360 00:22:08,280 --> 00:22:15,480 Well, for example, let's say two threads are attempting to write a property at the exact same time. 361 00:22:15,480 --> 00:22:21,060 Or let's say one thread is writing to a property while another thread is reading that property at the 362 00:22:21,060 --> 00:22:24,780 same time you are going to run into major problems. 363 00:22:24,780 --> 00:22:32,010 So to fix that issue, Roblox has made many properties and many functions in their API either read safe 364 00:22:32,010 --> 00:22:35,760 or write safe, or you can read and write to them, or you can't do any of it. 365 00:22:35,760 --> 00:22:40,740 And we can check by going to the Roblox documentation and looking at each one of the properties or functions 366 00:22:40,740 --> 00:22:46,560 for a particular thing in the Roblox API, and checking to see if it is safe to use in parallel. 367 00:22:46,560 --> 00:22:51,480 So inside of the Roblox documentation for parallel luoyue, there's a section called Thread Safety, 368 00:22:51,480 --> 00:22:57,420 and it says during the parallel execution, you can access most instances of the data model hierarchy 369 00:22:57,420 --> 00:23:02,070 as usual, but some API properties and functions aren't safe to read or write. 370 00:23:02,070 --> 00:23:07,710 If you use them in your parallel code, the Roblox engine can automatically detect and prevent these 371 00:23:07,740 --> 00:23:09,330 accesses from occurring. 372 00:23:09,510 --> 00:23:15,480 API members have a thread safety level that indicates whether and how you can use them in your parallel 373 00:23:15,480 --> 00:23:17,880 code, as the following table shows. 374 00:23:17,880 --> 00:23:23,880 So stuff in the Roblox API is going to be tagged with a particular safety level, determining if we 375 00:23:23,880 --> 00:23:25,650 are able to read or write to it. 376 00:23:25,650 --> 00:23:30,210 So for functions, if it is unsafe, we cannot call the function in parallel. 377 00:23:30,210 --> 00:23:34,590 But if it is labeled as safe, then we can go ahead and call the function in parallel. 378 00:23:34,590 --> 00:23:39,330 As an example, let's go ahead and look up the world route, which stores all of the functions for things 379 00:23:39,330 --> 00:23:41,550 like spatial queries and raycasting. 380 00:23:41,940 --> 00:23:47,130 And if we go ahead and take a look at one of the functions, for example, Raycasting, it tells us 381 00:23:47,130 --> 00:23:49,200 that it is read and write. 382 00:23:49,360 --> 00:23:49,630 Safe. 383 00:23:49,630 --> 00:23:54,250 So it says you can read and write to this property safely from multiple threads, meaning we can call 384 00:23:54,250 --> 00:23:57,700 this function while we are executing code in parallel. 385 00:23:57,700 --> 00:23:59,470 It is read and write safe. 386 00:23:59,470 --> 00:24:05,200 Now let's say we're looking through the base part class, and we want to figure out if we can set the 387 00:24:05,200 --> 00:24:08,650 anchored property while we are executing code in parallel. 388 00:24:08,650 --> 00:24:11,800 Well, if we head over to the anchor property, what does it tell us? 389 00:24:11,800 --> 00:24:18,040 Oh, it tells us this property is read only and it is safe to read in unsynchronized threads, which 390 00:24:18,040 --> 00:24:18,760 is parallel. 391 00:24:18,760 --> 00:24:23,830 But attempting to write to this property while we are executing in parallel will cause an error. 392 00:24:23,830 --> 00:24:26,620 So this is only read safe for the anchor property. 393 00:24:26,620 --> 00:24:29,290 And this is going to be true for almost every single property. 394 00:24:29,290 --> 00:24:34,690 For instances in your game, you'll be able to read them, but you will not be able to write to them. 395 00:24:34,690 --> 00:24:41,980 This is why we are forced to synchronize and go back into serial execution in order to, for example, 396 00:24:41,980 --> 00:24:47,080 set the C frame of a part or call the move to function on a humanoid. 397 00:24:47,080 --> 00:24:50,320 We can only do so while we are executing code in serial. 398 00:24:50,320 --> 00:24:53,470 Now what I'm going to do is I'm going to take my serial dear. 399 00:24:53,470 --> 00:24:58,090 I'm going to make sure to enable this script inside of him, and let me go ahead and move him over here 400 00:24:58,090 --> 00:24:59,800 and let's just duplicate him a bunch. 401 00:24:59,800 --> 00:25:02,770 So we'll make two of them and then we'll make four. 402 00:25:02,770 --> 00:25:04,210 So we'll have six here. 403 00:25:04,390 --> 00:25:07,900 Then we'll make 12 of them and let's keep duplicating him. 404 00:25:07,900 --> 00:25:10,840 So now we have a total of 24. 405 00:25:11,320 --> 00:25:12,730 Now we'll duplicate him again. 406 00:25:12,730 --> 00:25:13,810 Now we have 48. 407 00:25:13,810 --> 00:25:15,220 And let's duplicate him one more time. 408 00:25:15,220 --> 00:25:17,560 Now we have 72 dear. 409 00:25:17,590 --> 00:25:20,230 All executing their move. 410 00:25:20,230 --> 00:25:26,980 Two scripts, trying to do this random garbage and moving to the closest player's character. 411 00:25:26,980 --> 00:25:32,260 So if we go and playtest the game, what you're going to notice here is that the game is lagging like 412 00:25:32,260 --> 00:25:32,680 crazy. 413 00:25:32,680 --> 00:25:38,230 I have all these deer following me, and if we open up the micro profiler, the current frame time is 414 00:25:38,230 --> 00:25:40,300 averaging about 50 milliseconds. 415 00:25:40,300 --> 00:25:43,480 If we go ahead and pause and then let's go ahead and stop. 416 00:25:43,480 --> 00:25:46,090 And let's go ahead and take a look at one of these frames. 417 00:25:46,090 --> 00:25:48,250 Like here's this frame right here. 418 00:25:48,640 --> 00:25:53,950 Well what you're going to notice is that every single deer here it is a deer serial deer serial. 419 00:25:53,950 --> 00:25:57,850 They're all executing one after the other, one after the other. 420 00:25:57,850 --> 00:26:04,480 And this entire thread right here, RRB worker A is being completely overloaded, having to execute 421 00:26:04,480 --> 00:26:06,370 every single deer. 422 00:26:06,370 --> 00:26:10,450 And that's a problem because it's ruining the frame, time and performance of our game. 423 00:26:10,450 --> 00:26:14,170 So how can we split up each one of these tasks? 424 00:26:14,170 --> 00:26:18,880 So every single thread in our game is executing the functionality for some of these deer. 425 00:26:18,880 --> 00:26:22,300 Well that's where we can use parallel loop. 426 00:26:22,330 --> 00:26:24,640 So let me go ahead and delete these guys. 427 00:26:24,640 --> 00:26:27,700 And then let me disable the script in my serial deer. 428 00:26:28,280 --> 00:26:30,680 And let's go over to my parallel, dear. 429 00:26:30,680 --> 00:26:36,080 So remember, the only difference between these two is that one is executing the code in parallel. 430 00:26:36,080 --> 00:26:39,590 While this guy is executing the code in serial, it's the exact same code. 431 00:26:39,590 --> 00:26:45,200 So with this guy, let's go ahead and enable his script and let's make 72 parallel dears. 432 00:26:45,200 --> 00:26:46,280 So we'll duplicate here. 433 00:26:46,280 --> 00:26:50,360 We got two and we got four and we got six. 434 00:26:50,360 --> 00:26:51,620 Here is 12. 435 00:26:51,620 --> 00:26:53,270 And then here is our 48. 436 00:26:53,270 --> 00:26:54,890 And then here's our 72. 437 00:26:54,920 --> 00:26:57,890 So now we have 72 parallel dears. 438 00:26:57,890 --> 00:27:00,650 And let's see how the performance compares. 439 00:27:00,650 --> 00:27:02,870 Executing the same code in parallel. 440 00:27:03,320 --> 00:27:06,140 If you notice my game is no longer lagging. 441 00:27:06,140 --> 00:27:07,580 It looks very smooth. 442 00:27:07,580 --> 00:27:12,560 And that's because if we open up our micro profiler, look at the frame time, we are no longer averaging 443 00:27:12,560 --> 00:27:13,970 50 milliseconds of frame time. 444 00:27:13,970 --> 00:27:19,100 But now we are back to the nice frame time of around 16 milliseconds. 445 00:27:19,100 --> 00:27:19,970 Very very cool. 446 00:27:19,970 --> 00:27:23,720 And if we go ahead and pause and then let's go ahead and stop the game. 447 00:27:23,720 --> 00:27:24,920 What do you see? 448 00:27:24,920 --> 00:27:32,090 Well, what I see is that every single time task for each one of these dears has been split up across 449 00:27:32,090 --> 00:27:34,550 all of the different worker threads. 450 00:27:34,550 --> 00:27:37,430 So this guy is executing some dear functionality. 451 00:27:37,430 --> 00:27:39,860 This guy is executing some dear functionality. 452 00:27:39,860 --> 00:27:46,280 So as this guy and this guy, each one of our worker threads are being equally distributed code to execute 453 00:27:46,280 --> 00:27:47,240 in parallel. 454 00:27:47,240 --> 00:27:54,140 And that way we have now decreased the total frame time back to the average of around 16 milliseconds. 455 00:27:54,140 --> 00:28:01,040 So just by converting our script from serial to parallel, we were able to save our game from this performance 456 00:28:01,040 --> 00:28:02,060 degradation. 457 00:28:02,330 --> 00:28:07,130 Okay, so the last thing I want to show you in this video is how you can have actors communicate with 458 00:28:07,130 --> 00:28:08,180 other actors. 459 00:28:08,180 --> 00:28:13,490 So let me re-enable those two local scripts that I had and replicated first, and we'll call this first 460 00:28:13,490 --> 00:28:15,080 actor our actor number one. 461 00:28:15,080 --> 00:28:18,050 And we'll call this second actor our actor number two. 462 00:28:18,080 --> 00:28:20,360 How do we communicate between these two actors? 463 00:28:20,360 --> 00:28:25,970 Well, let's go ahead and grab each one of the respective actors for each script. 464 00:28:25,970 --> 00:28:30,740 So to get the actor for this local script, we'll make a variable called actor. 465 00:28:30,740 --> 00:28:32,750 And let's simply going to be equal to script. 466 00:28:32,750 --> 00:28:39,890 And our script has a function called get actor says returns the actor associated with our script, which 467 00:28:39,890 --> 00:28:45,080 is going to be actor one for our local script one, and it's going to be actor two for Local Script 468 00:28:45,080 --> 00:28:45,350 two. 469 00:28:45,350 --> 00:28:51,170 So let's do that for both of our local scripts and then inside of Local Script one. 470 00:28:51,170 --> 00:28:55,160 Let's go ahead and also grab the actor number two for the other script. 471 00:28:55,160 --> 00:28:58,130 So that's going to be equal to script dot parent dot parent. 472 00:28:58,130 --> 00:29:01,520 And let's go ahead and grab actor number two. 473 00:29:01,550 --> 00:29:04,010 Now to communicate with this actor. 474 00:29:04,010 --> 00:29:06,950 Let's go ahead and down here. 475 00:29:07,220 --> 00:29:09,080 Let's refer to this actor. 476 00:29:09,080 --> 00:29:13,430 And inside of this actor there's a function called send message. 477 00:29:13,430 --> 00:29:16,250 And it says sends a message to an actor. 478 00:29:16,250 --> 00:29:21,980 We give it a topic, the string of the message and then any other arguments we want to pass. 479 00:29:21,980 --> 00:29:27,080 So this is where we can send information between different scripts using actors. 480 00:29:27,080 --> 00:29:28,850 And that way inside of local script. 481 00:29:28,850 --> 00:29:34,700 Number two, we can listen to an event inside of our actor, and I believe it's called Bind to Message. 482 00:29:34,700 --> 00:29:35,330 There we go. 483 00:29:35,690 --> 00:29:37,430 So we call this function. 484 00:29:37,430 --> 00:29:40,700 We give it the topic and then a function to connect to that event. 485 00:29:40,700 --> 00:29:43,040 And we can also connect to it in parallel as well. 486 00:29:43,040 --> 00:29:45,530 So let's just connect to it normally. 487 00:29:45,530 --> 00:29:48,650 And cereal the topic what should our topic be. 488 00:29:48,650 --> 00:29:50,270 Let's just make our topic. 489 00:29:50,270 --> 00:29:50,930 Hello. 490 00:29:51,920 --> 00:29:53,480 We'll make the topic hello. 491 00:29:53,480 --> 00:29:56,930 And then we'll also connect to this hello topic. 492 00:29:57,530 --> 00:29:59,300 And we'll put a function here. 493 00:29:59,690 --> 00:30:05,870 And then we'll get any other information that is passed when another actor says hello to us. 494 00:30:05,870 --> 00:30:12,110 So when we call the send message function for this particular topic, let's give some information like 495 00:30:12,110 --> 00:30:13,280 one, two and three. 496 00:30:13,280 --> 00:30:17,000 And then inside of local script number two we can go ahead and print out the other script. 497 00:30:17,000 --> 00:30:17,870 Said hi to us. 498 00:30:17,870 --> 00:30:19,640 So hello. 499 00:30:19,640 --> 00:30:24,920 And then we could do something like printing out all of those other arguments into the console. 500 00:30:24,920 --> 00:30:26,600 Now for Local Script one. 501 00:30:26,600 --> 00:30:32,210 Let's actually go ahead and have a while loop down here, and let's send a message to our other actor 502 00:30:32,210 --> 00:30:33,170 every single second. 503 00:30:33,170 --> 00:30:36,350 So we'll do task dot wait one and then we'll send a message. 504 00:30:36,350 --> 00:30:41,570 And the reason I'm putting my task dot wait right here is because I don't know which one of these scripts 505 00:30:41,570 --> 00:30:43,790 is going to compile and execute first. 506 00:30:43,790 --> 00:30:47,030 So this script might execute and compile first. 507 00:30:47,030 --> 00:30:51,620 So that means when it goes to send a message, that first message is going to be lost. 508 00:30:51,620 --> 00:30:56,240 Because our second local script hasn't binded to that particular topic yet. 509 00:30:56,240 --> 00:31:02,150 But now that we have binded to this hello topic, and now we're sending a message with this topic of 510 00:31:02,150 --> 00:31:07,520 hello and some numbers, we should see the numbers one, two, three print out inside of the console. 511 00:31:07,520 --> 00:31:11,030 So let's go ahead and play test the game and there we go. 512 00:31:11,030 --> 00:31:12,140 Hello 123. 513 00:31:12,140 --> 00:31:16,760 And every single second goes by we are being sent a message through the actors. 514 00:31:16,760 --> 00:31:17,690 Very cool. 515 00:31:17,720 --> 00:31:23,330 Now if you want to be able to communicate between different scripts underneath actors using bindable 516 00:31:23,330 --> 00:31:27,590 events, then you can do so as well because the fire method for the bind. 517 00:31:27,910 --> 00:31:33,070 Event is read and write safe, so you can call this function while you're executing in parallel and 518 00:31:33,070 --> 00:31:34,960 you won't run into any errors. 519 00:31:35,660 --> 00:31:36,020 All right. 520 00:31:36,020 --> 00:31:38,630 So that was a lot of information we just learned. 521 00:31:38,630 --> 00:31:42,440 So your final question may be when should I use parallel Liu. 522 00:31:42,440 --> 00:31:43,820 And what should I use it on. 523 00:31:43,820 --> 00:31:49,670 Well you should use parallel Liu when you need to perform a lot of heavy calculations that will eat 524 00:31:49,670 --> 00:31:52,400 up your frame time if it was executed in serial. 525 00:31:52,400 --> 00:31:55,100 So, for example, we showed you how these dears. 526 00:31:55,100 --> 00:31:57,410 My serial deer ate up my frame time. 527 00:31:57,410 --> 00:32:02,030 But by splitting it into multiple different actors and executing it on multiple different threads, 528 00:32:02,030 --> 00:32:04,400 I was able to save the performance of my game. 529 00:32:04,400 --> 00:32:09,500 Same thing goes with my local scripts that were doing a bunch of garbage and causing the frame time 530 00:32:09,500 --> 00:32:10,220 to suffer. 531 00:32:10,220 --> 00:32:14,690 If I split those up between different actors, I was able to improve the frame time. 532 00:32:14,690 --> 00:32:19,160 Now, things that perform a lot of heavy calculations can be all types of things, such as some kind 533 00:32:19,160 --> 00:32:22,520 of raycasting system or a terrain generation system. 534 00:32:22,520 --> 00:32:28,160 Or maybe you have a bunch of pathfinding AI in your game, and in fact, parallel Liu can be great for 535 00:32:28,160 --> 00:32:32,210 pathfinding systems when you have dozens or maybe even hundreds of AI. 536 00:32:32,210 --> 00:32:37,100 Any time you need to do a bunch of expensive calculations that are going to cause your frame times to 537 00:32:37,100 --> 00:32:37,730 skyrocket. 538 00:32:37,730 --> 00:32:41,330 Yeah, you should try separating it into multiple different threads. 539 00:32:41,330 --> 00:32:45,770 If you don't have any performance problems currently in your projects, or you don't have any crazy 540 00:32:45,770 --> 00:32:50,780 math calculations and everything in your game is working fine on serial, then great, you don't need 541 00:32:50,780 --> 00:32:52,550 to convert any of it into parallel. 542 00:32:52,550 --> 00:32:57,650 Only when you notice that you can improve the performance of your games by executing code in parallel, 543 00:32:57,650 --> 00:32:58,880 should you use it? 544 00:32:58,880 --> 00:33:01,400 Otherwise, that's all for me for this lecture. 545 00:33:01,400 --> 00:33:06,770 I hope you now have a better understanding of parallel Liu, as the documentation can be quite confusing 546 00:33:06,770 --> 00:33:10,040 and there are not many good resources out there to teach parallel. 547 00:33:10,040 --> 00:33:10,610 Liu. 548 00:33:10,640 --> 00:33:12,020 Thank you for watching.